package io.github.iogogogo.commons.util;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.github.iogogogo.commons.support.JacksonConfig;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.util.Collection;

/**
 * Created by tao.zeng on 2024/11/7.
 */
@Slf4j
public class JsonParse {

    /**
     * The constant EMPTY_OBJECT.
     */
    private final static String EMPTY_OBJECT = "{}";
    /**
     * The constant EMPTY_LIST.
     */
    private final static String EMPTY_LIST = "[]";

    /**
     * The constant OBJECT_MAPPER.
     */
    private final static ObjectMapper OBJECT_MAPPER = JacksonConfig.getInstance().buildObjectMapper();


    /**
     * 将Java对象转换为JSON字符串
     * 此方法使用全局的ObjectMapper实例将给定的Java对象序列化为JSON格式的字符串
     * 主要用于数据交换和存储
     *
     * @param bean 待转换的Java对象可以是任何实现了Serializable接口的对象
     * @return 返回表示该对象的JSON格式字符串
     * @throws JsonProcessingException 如果对象无法被序列化为JSON字符串，则抛出此异常
     */
    public static String toJson(Object bean) throws JsonProcessingException {
        return OBJECT_MAPPER.writeValueAsString(bean);
    }


    /**
     * 尝试将一个Java对象转换为JSON字符串
     * 此方法主要用于简化JSON转换过程，并处理可能发生的异常
     *
     * @param bean 待转换的Java对象如果传入的是一个Collection类型，则在转换失败时返回空列表字符串；
     *             否则，返回空对象字符串
     * @return 转换后的JSON字符串如果转换失败且输入为Collection类型，则返回"EMPTY_LIST"；
     * 否则，返回"EMPTY_OBJECT"
     */
    public static String tryToJson(Object bean) {
        try {
            // 尝试将Java对象转换为JSON字符串
            return toJson(bean);
        } catch (JsonProcessingException e) {
            // 捕获JSON处理异常，并记录日志
            log.error("JsonProcessingException,", e);
        }
        // 根据输入对象类型返回相应的空字符串表示
        return bean instanceof Collection ? EMPTY_LIST : EMPTY_OBJECT;
    }


    /**
     * 将Java对象转换为格式化的JSON字符串
     * 此方法使用ObjectMapper将给定的Java对象序列化为JSON格式，并进行格式化以提高可读性
     * 如果序列化过程中遇到JsonProcessingException，将记录警告信息并返回空字符串或空对象
     *
     * @param bean 要转换为JSON字符串的Java对象
     * @return 格式化的JSON字符串，如果输入是一个空集合则返回空字符串，如果输入为空则返回空对象
     */
    public static String toJsonPrettyPrinter(Object bean) {
        try {
            // 使用ObjectMapper的writerWithDefaultPrettyPrinter方法来序列化并格式化JSON
            return OBJECT_MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(bean);
        } catch (JsonProcessingException e) {
            // 捕获并记录JsonProcessingException异常
            log.warn("JsonProcessingException,", e);
        }
        // 根据输入对象的类型，返回相应的空字符串或空对象
        return bean instanceof Collection ? EMPTY_LIST : EMPTY_OBJECT;
    }


    /**
     * 将JSON字符串解析为指定类型的对象
     *
     * @param json 包含对象数据的JSON字符串
     * @param clz  需要解析的对象的类类型
     * @return 解析后的对象实例，如果JSON字符串为空，则返回null
     * @throws IOException 如果解析过程中发生错误
     */
    public static <T> T parse(String json, Class<T> clz) throws IOException {
        // 检查输入的JSON字符串是否为空，如果为空则直接返回null
        if (StringUtils.isEmpty(json)) return null;
        // 使用ObjectMapper将JSON字符串解析为指定类型的对象
        return OBJECT_MAPPER.readValue(json, clz);
    }


    /**
     * 将JSON字符串解析为指定类型的对象
     * <p>
     * 此方法使用了泛型，以便能够解析成任意类型的对象
     * 它依赖于一个外部的OBJECT_MAPPER实例来执行实际的解析工作
     *
     * @param json 包含对象数据的JSON字符串
     * @param type 要解析的目标类型，使用TypeReference指定
     * @param <T>  泛型参数，表示要解析的对象类型
     * @return 解析后的对象实例，如果json为空字符串，则返回null
     * @throws IOException 如果解析过程中发生错误，会抛出此异常
     */
    public static <T> T parse(String json, TypeReference<T> type) throws IOException {
        // 检查输入的JSON字符串是否为空，如果为空则直接返回null
        if (StringUtils.isEmpty(json)) return null;
        // 使用OBJECT_MAPPER读取JSON字符串，并解析为指定类型的对象
        return OBJECT_MAPPER.readValue(json, type);
    }


    /**
     * 尝试解析JSON字符串为指定类型的对象
     * 如果JSON字符串为空或解析过程中出现异常，则返回null
     * 此方法主要用于在不需要严格异常处理的情况下，快速将JSON字符串转换为对象
     *
     * @param json 待解析的JSON字符串
     * @param clz  目标对象类型
     * @param <T>  泛型参数，表示目标对象类型
     * @return 解析成功的对象或null
     */
    public static <T> T tryParse(String json, Class<T> clz) {
        try {
            // 使用ObjectMapper尝试解析JSON字符串为指定类型的对象
            return parse(json, clz);
        } catch (IOException e) {
            // 捕获IOException异常，记录日志，然后返回null
            log.warn("IOException,", e);
        }
        // 如果发生异常或解析失败，则返回null
        return null;
    }


    /**
     * 尝试解析JSON字符串为指定类型的对象
     * <p>
     * 此方法主要用于处理JSON字符串解析操作，当解析失败时（如JSON字符串为空或格式不匹配），
     * 方法会返回null，避免因解析异常导致的程序中断执行
     * <blockquote>
     * <pre>
     *     String json = "{\"name\":\"test\",\"age\":18}";
     *     Map<String, Object> map = tryParse(json, new TypeReference<Map<String, Object>>() {});
     *
     * </pre>
     * </blockquote>
     *
     * @param json 待解析的JSON字符串
     * @param type 目标对象类型引用，用于指定解析后的对象类型
     * @param <T>  泛型参数，表示目标对象类型
     * @return 解析成功则返回指定类型对象，失败则返回null
     */
    public static <T> T tryParse(String json, TypeReference<T> type) {
        try {
            // 使用ObjectMapper尝试解析JSON字符串为指定类型对象
            return parse(json, type);
        } catch (IOException e) {
            // 捕获IOException异常，记录日志，避免因异常导致程序中断
            log.warn("IOException,", e);
        }
        // 解析失败或发生异常时返回null
        return null;
    }
}
